#-*- codeing = utf-8 -*-
#@Time : 2020/11/26 12:32
#@Author : 阳某
#@File : 01.哈希.py
#@Software : PyCharm
'''
哈希函数
概念
哈希，英文叫做 hash。

哈希函数（hash function）可以把 任意长度的数据（字节串）计算出一个为固定长度的结果数据。

我们习惯把 要计算 的数据称之为 源数据， 计算后的结果数据称之为 哈希值（hash value）或者 摘要(digests)。

有好几种哈希函数，对应不同的算法， 常见有的 MD5, SHA1, SHA224, SHA256, SHA384, SHA512

哈希计算的特点是：

相同的 源数据， 采用 相同的哈希算法， 计算出来的哈希值 一定相同

不管 源数据 有多大，相同的哈希算法，计算出来的哈希值长度 都是一样长的。 、

算法	计算结果长度
MD5	16字节
SHA1	20字节
SHA224	28字节
SHA256	32字节
SHA384	48字节
SHA512	64字节
算法不可逆。

也就是说，不能通过 哈希值 反过来计算出 源数据。 所以哈希和我们常说的加密解密不同。

不同的源数据 使用同样的哈希算法，可能会产生相同的 哈希值，这被称之为碰撞率（collision rate）

各种哈希算法，计算的结果长度越长，碰撞率越低，通常耗费的计算时长也越长。

即使是 MD5 算法， 碰撞率也 非常小，小到几乎可以忽略不计。大约是 1.47*10的负29次方

'''

import hashlib

# 使用 md5 算法
m = hashlib.md5()

# 要计算的源数据必须是字节串格式
# 字符串对象需要encode转化为字节串对象
m.update("张三，学费已交|13ty8ffbs2v".encode())

# 产生哈希值对应的bytes对象
resultBytes = m.digest()
# 产生哈希值的十六进制表示
resultHex   = m.hexdigest()
print(resultHex)

# 如果你想使用别的哈希算法，比如， sha256 算法，只需要修改为对应的函数 sha256()即可
# 如下  其它都不用变。
# import hashlib
# m = hashlib.sha256()


'''
加密解密
简介
加解密算法，是对源数据 进行运算产生加密数据，以及反向过程，对加密数据反算出 源数据。

加解密算法 和 hash算法 不同点有：

加解密算法 是可逆的，hash算法是不可逆的。

hash算法可以对很大的数据产生比较小的哈希值，而加密算法源数据很大，加密后的数据也会很大

加解密算法 可以分为 对称加密 以及 不对称加密

对称加密 指 加密和解密 使用相同的 密钥 。

而 不对称加密 指 加密和解密 使用不同的 密钥，通常是一对密钥，称之为公钥（用来加密）和私钥（用来解密）。

比较常见的 对称加密算法有： AES, RC4, DES, 3DES, IDEA 等。

其中安全等级较高的是 AES。 关于加密算法安全性可以参考这篇文章

而最知名的 不对称加密系统 就是 RSA (Rivest–Shamir–Adleman) 。

Python语言加解密
计算哈希值， Python有内置的库。

但是，加解密的库，Python 没有内置的。我们需要安装使用第三方开发的库。

目前口碑比较好的Python加解密库有 cryptography PyNaCl

这里，我们以使用比较广泛的 cryptography 为例（Paramiko就使用该库作为底层加解密计算），展示如何进行加解密。

首先，我们执行命令 pip install cryptography 安装该库。

注意：由于Paramiko就使用该库作为底层加解密计算，如果你已经安装了Paramiko，cryptography库肯定已经安装好了。 就会显示 Requirement already satisfied
下面是一个使用 该库进行 AES 加解密运算的例子

from cryptography.fernet import Fernet
'''


from cryptography.fernet import Fernet

# 产生密钥， 密钥是加密解密必须的
key = Fernet.generate_key()
f = Fernet(key)


src = "白月黑羽网站学习Python真好啊"
# 源信息，必须是字节串对象
# 字符串对象需要encode一下
srcBytes = src.encode()

# 生成加密字节串
token = f.encrypt(srcBytes)
print(token)

# 解密，返回值是字节串对象
sb = f.decrypt(token)
print(sb.decode())